package top.jolyoulu.utils;

import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @Author: JolyouLu
 * @Date: 2021/9/14 17:26
 * @Version 1.0
 */
public class TimerUtils<B, R> {

    public static void timer(Task task) {
        long startTime = startTime();
        task.run();
        printNanoTime(endTime(startTime));
    }

    /**
     * 计时
     *
     * @param before 准备参数阶段，将准备好的参数return
     * @param task   计算阶段，接收一个参数 before阶段return的参数内容
     * @param after  结果阶段，接收一个参数 task阶段return的参数内容
     */
    public void timer(Before<B> before, Call<B, R> task, After<R> after) {
        B args = before.before();
        System.out.print("入参 ==> ");
        printObject(args);
        long startTime = startTime();
        R run = task.call(args);
        printNanoTime(endTime(startTime));
        after.after(run);
        System.out.print("结果 ==> ");
        printObject(run);
    }

    /**
     * 开始计时
     *
     * @return 开始时间
     */
    private static long startTime() {
        return System.nanoTime();
    }

    /**
     * 结束即时
     *
     * @param startTime 开始时间
     * @return 耗时
     */
    private static long endTime(long startTime) {
        return System.nanoTime() - startTime;
    }

    /**
     * 格式化打印时间
     *
     * @param duration 时长
     */
    private static void printNanoTime(long duration) {
        String sb = new StringBuilder()
                .append("耗时 => ")
                .append(TimeUnit.MILLISECONDS.convert(duration, TimeUnit.NANOSECONDS)).append(" 毫秒")
                .append(" | ")
                .append(duration).append(" 纳秒")
                .toString();
        System.out.println(sb);
    }

    /**
     * 打印参数
     */
    private static void printObject(Object obj) {
        if (obj instanceof int[]) {
            System.out.println(Arrays.toString((int[]) obj));
        } else if (obj instanceof long[]) {
            System.out.println(Arrays.toString((long[]) obj));
        } else {
            System.out.println(obj.toString());
        }
    }

    public interface Task {
        void run();
    }

    public interface Before<B> {
        B before();
    }

    public interface Call<B, R> {
        R call(B args);
    }

    public interface After<R> {
        void after(R res);
    }
}
